/*//////////////////////////////////////////////////////////////////////////////
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2004-2008 Intel Corporation. All Rights Reserved.
//
//
//
//
*/

#include "umc_defs.h"

#if defined (UMC_ENABLE_AAC_INT_AUDIO_ENCODER)

#include <string.h>
#include "umc_audio_codec.h"
#include "aac_enc_int.h"
#include "umc_aac_encoder_params.h"
#include "umc_aac_encoder_int.h"
#include "ipps.h"

#include "aaccmn_adts.h"
#include "aaccmn_adif.h"

namespace UMC {

#ifdef  __cplusplus
extern "C" {
#endif

void enc_adts_header(sAdts_fixed_header *pFixedHeader,
                     sAdts_variable_header *pVarHeader,
                     sBitsreamBuffer *pBS);

void enc_adif_header(sAdif_header* pAdifHeader,
                     sBitsreamBuffer* pBS);

#ifdef  __cplusplus
}
#endif

/****************************************************************************/

AudioCodec *CreateAACEncoderInt() { return (new AACEncoderInt); }

AACEncoderInt::AACEncoderInt()
{
  state = NULL;
  stateMemId = 0;
}

/****************************************************************************/

AACEncoderInt::~AACEncoderInt()
{
  Close();
}

/****************************************************************************/

IppBool AACEncoderInt::CheckBitRate(Ipp32s br,
                                    Ipp32s& /*ind*/)
{
  if (br <= 0)
    return ippFalse;

  return ippTrue;
}

/****************************************************************************/

Status AACEncoderInt::Init(BaseCodecParams* init)
{
  AACEncoderParams* pAACEncoderParams;
  AudioCodecParams* pAudioCodecInit =
    DynamicCast<AudioCodecParams, BaseCodecParams>(init);
  Ipp32s m_ns_mode;
  Ipp32s m_stereo_mode;
  Ipp32s stereo_mode;
  Ipp32s size = 0;

  if (!pAudioCodecInit) return UMC_ERR_NULL_PTR;

  m_sampling_frequency = pAudioCodecInit->m_info_in.sample_frequency;
  m_channel_number = pAudioCodecInit->m_info_in.channels;
  m_bitrate = pAudioCodecInit->m_info_out.bitrate;
  m_pts_prev = 0;

  pAACEncoderParams = DynamicCast<AACEncoderParams, BaseCodecParams>(init);
  if (pAACEncoderParams) {
    m_audioObjectType = pAACEncoderParams->audioObjectType;
    m_stereo_mode = pAACEncoderParams->stereo_mode;
    m_ns_mode = pAACEncoderParams->ns_mode;
    m_outputFormat = pAACEncoderParams->outputFormat;
  } else {
    m_audioObjectType = AOT_AAC_LC;
    m_stereo_mode = UMC_AAC_LR_STEREO;
    m_ns_mode = 0;
    m_outputFormat = UMC_AAC_ADTS;
  }

  if ((m_outputFormat != UMC_AAC_ADTS) && (m_outputFormat != UMC_AAC_ADIF))
    return UMC_ERR_INIT;

  if ((m_audioObjectType != AOT_AAC_LC) && (m_audioObjectType != AOT_AAC_LTP))
    return UMC_ERR_INIT;

  if (m_outputFormat == UMC_AAC_ADTS) {
    if ((m_channel_number == 7) || (m_channel_number > 8))
      return UMC_ERR_INIT;
  }

  if      (m_audioObjectType == AOT_AAC_MAIN) m_adtsProfile = 0;
  else if (m_audioObjectType == AOT_AAC_LC)   m_adtsProfile = 1;
  else if (m_audioObjectType == AOT_AAC_SSR)  m_adtsProfile = 2;
  else if (m_audioObjectType == AOT_AAC_LTP)  m_adtsProfile = 3;
  else                                        m_adtsProfile = 4;

  m_adtsID = 1;
  if (m_adtsProfile >= 3) m_adtsID = 0;

  if (m_channel_number == 1)
    m_stereo_mode = UMC_AAC_MONO;

  switch (m_stereo_mode) {
      case UMC_AAC_MONO:
        stereo_mode = AAC_MONO; break;
      case UMC_AAC_LR_STEREO:
        stereo_mode = AAC_LR_STEREO; break;
      case UMC_AAC_MS_STEREO:
        stereo_mode = AAC_MS_STEREO; break;
      case UMC_AAC_JOINT_STEREO:
        stereo_mode = AAC_JOINT_STEREO; break;
      default:
        stereo_mode = AAC_LR_STEREO; break;
  };

  if (aaciencInit(NULL, m_sampling_frequency, m_channel_number,
                  m_bitrate, m_audioObjectType, stereo_mode, m_ns_mode, &size) != AAC_OK)
    return UMC_ERR_INIT;

  // checks or create memory allocator;
  if (BaseCodec::Init(init) != UMC_OK) {
    vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("Failed to create allocator!\n"));
    return UMC_ERR_ALLOC;
  }

  if (m_pMemoryAllocator->Alloc(&stateMemId, size,
                                UMC_ALLOC_PERSISTENT) != UMC_OK) {
    vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External allocation failed\n"));
    return UMC_ERR_ALLOC;
  }

  state = (AACEnc *)m_pMemoryAllocator->Lock(stateMemId);
  if (!state) {
    vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed\n"));
    return UMC_ERR_ALLOC;
  }

  if (aaciencInit(state, m_sampling_frequency, m_channel_number,
      m_bitrate, m_audioObjectType, stereo_mode, m_ns_mode, &size) != AAC_OK)
    return UMC_ERR_INIT;

  MemUnlock();

  return UMC_OK;
}

/****************************************************************************/

Status AACEncoderInt::Close()
{
  if (state == NULL)
    return UMC_OK;

  if( m_pMemoryAllocator == NULL ) {
    return UMC_OK;
  }

  if (state) {
    m_pMemoryAllocator->Free(stateMemId);
    state = NULL;
  }
  BaseCodec::Close();
  return UMC_OK;
}

/****************************************************************************/

Status AACEncoderInt::GetInfo(BaseCodecParams* info)
{
  if (!info) return UMC_ERR_NULL_PTR;

  info->m_SuggestedInputSize = m_channel_number*sizeof(Ipp16s)*1024;
  info->m_SuggestedOutputSize = (768*m_channel_number+9/* ADTS_HEADER */)*sizeof(Ipp8u);

  AudioCodecParams* pAudioCodecInfo =
    DynamicCast<AudioCodecParams, BaseCodecParams>(info);

  if (!pAudioCodecInfo) return UMC_OK;

  pAudioCodecInfo->m_info_in.bitPerSample      = 16;
  pAudioCodecInfo->m_info_out.bitrate          = m_bitrate;
  pAudioCodecInfo->m_info_in.channels          = m_channel_number;
  pAudioCodecInfo->m_info_out.channels         = m_channel_number;

  pAudioCodecInfo->m_info_in.sample_frequency  = m_sampling_frequency;
  pAudioCodecInfo->m_info_out.sample_frequency = m_sampling_frequency;
  pAudioCodecInfo->m_info_in.stream_type       = PCM_AUDIO;
  pAudioCodecInfo->m_info_out.stream_type      = AAC_AUDIO;

  return UMC_OK;
}

/****************************************************************************/

Status AACEncoderInt::GetFrame(MediaData* in,
                               MediaData* out)
{
  Ipp8u                 *outPointer;
  AACStatus             result;
  Ipp32s                nSamples;
  Ipp32s                nEncodedBytes, headerBytes;
  sBitsreamBuffer       BS;
  sBitsreamBuffer       *pBS = &BS;
  sAdts_fixed_header    adts_fixed_header;
  sAdts_variable_header adts_variable_header;
  Ipp32s                sampling_frequency_index;
  //Ipp32s                rested_bytes;
  Ipp64f                pts_start, pts_end;
  Status status;

  if (!in || !out)
    return UMC_ERR_NULL_PTR;

  nSamples = (in->GetDataSize()/sizeof(Ipp16s));
  if (nSamples < m_channel_number * 1024)
    return UMC_ERR_NOT_ENOUGH_DATA;

  nSamples = m_channel_number * 1024;

  outPointer = (Ipp8u *)out->GetDataPointer();

  status = MemLock();
  if (status != UMC_OK) {
    vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
    return status;
  }

  if (state == NULL)
    return UMC_ERR_NOT_INITIALIZED;

  result = aaciencGetSampleFrequencyIndex(&sampling_frequency_index, state);

  INIT_BITSTREAM(pBS, outPointer)

  if (m_outputFormat == UMC_AAC_ADTS) {
    // Put to bistream ADTS header !
    // Fixed header.
    adts_fixed_header.ID = m_adtsID;
    adts_fixed_header.Layer = 0;
    adts_fixed_header.protection_absent = 1;
    adts_fixed_header.Profile = m_adtsProfile;
    adts_fixed_header.sampling_frequency_index = sampling_frequency_index;
    adts_fixed_header.private_bit = 0;
    adts_fixed_header.channel_configuration = m_channel_number;
    if (m_channel_number == 8)
      adts_fixed_header.channel_configuration = 7;
    adts_fixed_header.original_copy = 0;
    adts_fixed_header.Home = 0;

    // Variable header !
    adts_variable_header.copyright_identification_bit = 0;
    adts_variable_header.copyright_identification_start = 0;
    adts_variable_header.aac_frame_length = 0;
    adts_variable_header.adts_buffer_fullness = 0x7FF;
    adts_variable_header.no_raw_data_blocks_in_frame = 0;

    enc_adts_header(&adts_fixed_header, &adts_variable_header, pBS);
    if (adts_fixed_header.protection_absent == 0) {
      PUT_BITS(pBS,0,16); /* for CRC */
    }
  } else if (m_outputFormat == UMC_AAC_ADIF) {
    Ipp32s frame_number;
    result = aaciencGetNumEncodedFrames(&frame_number, state);
    if (frame_number == 0) {
      sAdif_header adif_header;
      adif_header.adif_id = ADIF_SIGNATURE;
      adif_header.copyright_id_present = 0;
      adif_header.original_copy = 0;
      adif_header.home = 0;
      adif_header.bitstream_type = 1;
      adif_header.bitrate = m_bitrate;
      adif_header.num_program_config_elements = 0;
      aaciencFillProgramConfigElement(adif_header.pce, 0, state);
      enc_adif_header(&adif_header, pBS);
    }
  }

  SAVE_BITSTREAM(pBS)
  Byte_alignment(pBS);

  GET_BITS_COUNT(pBS, headerBytes)
  headerBytes >>= 3;

  result = aaciencGetFrame((Ipp16s *)in->GetDataPointer(),
                           &nEncodedBytes, outPointer + headerBytes, state);

  if (m_outputFormat == UMC_AAC_ADTS) {
    Ipp32u  *crc_ptr;
    Ipp32s  crc_offset;
    adts_variable_header.aac_frame_length = headerBytes + nEncodedBytes;

    INIT_BITSTREAM(pBS, outPointer)
    crc_ptr = BS.pCurrent_dword;
    crc_offset = BS.nBit_offset;
    enc_adts_header(&adts_fixed_header, &adts_variable_header, pBS);
    if (adts_fixed_header.protection_absent == 0) {
      sCrcSaveTable *crcSaveTable;
      Ipp32u crc;
      Ipp32s i;

      bs_CRC_reset(&crc);
      aaciencGetCrcSaveTable(&crcSaveTable, state);

      bs_CRC_update(crc_ptr, crc_offset, (headerBytes - 2) * 8, &crc);
      for (i = 0; i < m_channel_number; i++) {
        bs_CRC_update(crcSaveTable[i].crc_ptr, crcSaveTable[i].crc_offset,
          crcSaveTable[i].crc_len, &crc);
        bs_CRC_update_zero(crcSaveTable[i].crc_zero_len, &crc);
      }
      PUT_BITS(pBS,crc,16);
      SAVE_BITSTREAM(pBS)
    }
  }

  pts_start = in->GetTime();

  if (pts_start < 0)
    pts_start = m_pts_prev;

  //rested_bytes = in->GetDataSize()- nSamples*sizeof(Ipp16s);
  pts_end = pts_start+(Ipp32f)(nSamples/m_channel_number)/(Ipp32f)(m_sampling_frequency);

  out->SetTime(pts_start, pts_end);
  out->SetDataSize(headerBytes + nEncodedBytes);
  in->MoveDataPointer(nSamples*sizeof(Ipp16s));

  //if (rested_bytes) {
    in->SetTime(pts_end);
  //}

  MemUnlock();
  return UMC_OK;
}

/****************************************************************************/

#ifdef NO_FLOAT
Status AACEncoderInt::GetDuration(Ipp32f* p_duration)
{
    Ipp32f duration;

    if (!p_duration)
        return UMC_ERR_NULL_PTR;

//    duration = (Ipp32f)m_frame_num * 1024;
//    duration /= sampleRate;

    duration = -1;

    p_duration[0] = duration;

    return UMC_OK;
}
#endif

/****************************************************************************/

Status AACEncoderInt::MemLock() {
  AACEnc *pOldState = state;
  state = (AACEnc *)m_pMemoryAllocator->Lock(stateMemId);
  if(!state) {
    vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed\n"));
    return UMC_ERR_ALLOC;
  }
  if (state != pOldState) {
    aaciencUpdateMemMap(state, (Ipp8u *)state-(Ipp8u *)pOldState);
  }
  return UMC_OK;
}

/****************************************************************************/

Status AACEncoderInt::MemUnlock() {
  if (stateMemId) {
    if (m_pMemoryAllocator->Unlock(stateMemId) != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Unlock failed\n"));
      return UMC_ERR_ALLOC;
    }
  }
  return UMC_OK;
}

/****************************************************************************/

};//namespace UMC

#endif //UMC_ENABLE_AAC_INT_AUDIO_ENCODER

